package com.example.chat.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.chat.domain.entity.ChatContent;
import com.example.chat.domain.entity.ChatMessage;
import com.example.chat.domain.entity.User;
import com.example.chat.mapper.ChatMapper;
import com.example.chat.result.Result;
import com.example.chat.service.ChatService;
import com.example.chat.service.UserService;
import com.example.chat.utils.RedisConstants;
import com.example.chat.utils.UserHolder;
import com.example.chat.domain.vo.ChatObjectRedis;
import com.example.chat.domain.vo.QueryChatListVo;
import com.example.chat.domain.vo.QueryContentVo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ChatServiceImpl extends ServiceImpl<ChatMapper, ChatMessage> implements ChatService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private UserService userService;

    private static final DefaultRedisScript<Void> test = new DefaultRedisScript<>();
    private static final DefaultRedisScript<Void> del = new DefaultRedisScript<>();
    static {
        test.setLocation(new ClassPathResource("scripts/saveMessage.lua"));
        test.setResultType(Void.class);
        del.setLocation(new ClassPathResource("scripts/deleteChatList.lua"));
        del.setResultType(Void.class);
    }

    @Override
    public Result getChatList() {
        Long currentId = UserHolder.getCurrentId();
        Set<String> idSet = stringRedisTemplate.opsForZSet().reverseRange(RedisConstants.CHAT_LIST + currentId, 0, -1);
        if(idSet == null || idSet.size() < 1){
            return Result.ok(Collections.emptyList());
        }
        List<Long> ids = idSet.stream().map(Long::valueOf).collect(Collectors.toList());
        String idStr = StrUtil.join(",",ids);
        List<QueryChatListVo> chatListItemVos = userService.query()
                .in("id", ids).last("order by field(id," + idStr + ")").list()
                .stream()
                .map(user -> BeanUtil.copyProperties(user, QueryChatListVo.class))
                .collect(Collectors.toList());

        for (int i = 0; i < ids.size(); i++) {
            Map<Object, Object> objectObjectMap = stringRedisTemplate.opsForHash()
                    .entries(RedisConstants.CHAT_OBJECT + currentId + ":" + ids.get(i));
            ChatObjectRedis chatObjectRedis = BeanUtil.toBean(objectObjectMap, ChatObjectRedis.class);
            BeanUtil.copyProperties(chatObjectRedis,chatListItemVos.get(i));
        }
        return Result.ok(chatListItemVos);
    }

    @Override
    public Long saveMessage(String message,long sender,Long receiver) {
        ChatMessage chatMessage = new ChatMessage();
        chatMessage.setSender(sender);
        chatMessage.setReceiver(receiver);
        chatMessage.setMessage(message);
        boolean save = save(chatMessage);
        Long msgId = chatMessage.getId();
        if(save){
            String time = LocalDateTime.now()
                    .format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));
            List<String> keys = List.of(String.valueOf(sender),String.valueOf(receiver));
            List<String> args = List.of(msgId.toString(), String.valueOf(System.currentTimeMillis()),
                    message, time);
            stringRedisTemplate.execute(test,keys,args.toArray());
        }
        return msgId;
    }

    @Override
    public String forWard(Long msgId,String message,Long sender,Long receiver) {
        User user = userService.getById(sender);
        ChatContent chatContent  = new ChatContent();
        chatContent.setId(msgId);
        chatContent.setReceiver(receiver);
        chatContent.setText(message);
        chatContent.setUserId(user.getId());
        chatContent.setUserName(user.getUserName());
        chatContent.setIcon(user.getIcon());
        chatContent.setType(1);
        chatContent.setTime(LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));
        return JSONUtil.toJsonStr(chatContent);
    }

    @Override
    public Result tipClearOrReduce(Long id,Integer type) {
        Long currentId = UserHolder.getCurrentId();
        if(Integer.valueOf(0).equals(type)){
            stringRedisTemplate.opsForHash()
                    .put(RedisConstants.CHAT_OBJECT+currentId+":"+id,
                            RedisConstants.CHAT_OBJECT_Tip,"0");
//            log.info("清空tip值");
            return Result.ok();
        }
        stringRedisTemplate.opsForHash()
                .increment(RedisConstants.CHAT_OBJECT+currentId+":"+id,RedisConstants.CHAT_OBJECT_Tip,-1);
//        log.info("tip值减一");
        return Result.ok();
    }

    @Override
    public Result queryContent(Long userId, Long timestamp, Integer offset) {
        Long currentId = UserHolder.getCurrentId();
        QueryContentVo queryContentVo = new QueryContentVo();
        Integer queryCount = 10;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(RedisConstants.CHAT_CHATCONTENT + currentId + ":" + userId,
                        0, timestamp, offset, queryCount);
        if(typedTuples == null || typedTuples.isEmpty()){
            queryContentVo.setRemain(false);
            return Result.ok(queryContentVo);
        }
        List<Long> msgIds = new ArrayList<>();
        Long tempTimastamp = 0L;
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            msgIds.add(Long.valueOf(typedTuple.getValue()));
            tempTimastamp = typedTuple.getScore().longValue();
        }
        queryContentVo.setRemain(queryCount.equals(msgIds.size()));
        queryContentVo.setTimestamp(tempTimastamp);
        List<ChatMessage> chatMessageList = lambdaQuery().in(ChatMessage::getId, msgIds).list();
        List<ChatContent> fullMessageList = getFullMessageList(currentId,userId, chatMessageList);
        queryContentVo.setContents(fullMessageList);
        return Result.ok(queryContentVo);
    }

    @Transactional
    public Result deleteChatListById(Long id) {
        Long currentId = UserHolder.getCurrentId();
        List<String> keys = List.of(currentId.toString(),id.toString());
        stringRedisTemplate.execute(del,keys);
        return Result.ok();
    }

    private List<ChatContent> getFullMessageList(Long currentId,Long userId, List<ChatMessage> chatMessageList) {
        List<Long> userIds = List.of(currentId, userId);
        String strIds = StrUtil.join(",", userIds);
        //查询两个用户，并按顺序输出，users.get(0)为当前用户，users.get(1)为对方
        List<User> users = userService.lambdaQuery()
                .in(User::getId,userIds)
                .last("order by field(id," + strIds + ")")
                .list();
        List<ChatContent> chatList = new ArrayList<>();
        for (ChatMessage chatMessage : chatMessageList) {
            //设置公共基本信息，id,text，time
            ChatContent queryChatContentVo = new ChatContent();
            queryChatContentVo.setText(chatMessage.getMessage());
            queryChatContentVo.setTime(chatMessage.getCreateTime());
            queryChatContentVo.setId(chatMessage.getId());
            //根据是否是本人发的内容设置不同userId,icon,type
            if(currentId.equals(chatMessage.getSender())){
                queryChatContentVo.setType(2);
                queryChatContentVo.setUserId(users.get(0).getId());
                queryChatContentVo.setIcon(users.get(0).getIcon());
            }else{
                queryChatContentVo.setType(1);
                queryChatContentVo.setUserId(users.get(1).getId());
                queryChatContentVo.setIcon(users.get(1).getIcon());
            }
            chatList.add(queryChatContentVo);
        }
        return chatList;
    }

}
